package com.xxxtea.sdk.amap;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.dtflys.forest.annotation.*;
import com.dtflys.forest.callback.DefaultSuccessWhen;
import com.dtflys.forest.exceptions.ForestRuntimeException;
import com.dtflys.forest.http.ForestQueryMap;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestResponse;
import com.dtflys.forest.interceptor.Interceptor;
import com.xxxtea.dto.Location;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 高德地图SDK客户端
 * 
 * 封装高德地图Web服务API的常用接口，包括：
 * - 经纬度逆地理编码
 * - 地址正地理编码
 * - 路线规划
 * - 距离计算
 * 
 * 支持Forest HTTP客户端注解式调用，自动重试、日志、拦截器等功能
 * 
 * @author badman
 * @since 1.0.0
 */
@Retry(maxRetryCount = "3")
@LogEnabled(logResponseContent = true)
@Success(condition = AmapClient.AmapSuccessCondition.class)
@BaseRequest(baseURL = "https://restapi.amap.com", interceptor = AmapClient.AmapInterceptor.class)
public interface AmapClient {

	/**
	 * 根据经度和纬度获取地理信息（逆地理编码）
	 * @param longitude 经度
	 * @param latitude 纬度
	 * @return 逆地理编码结果
	 */
	@ApiOperation("根据经度和纬度获取地理信息")
	@JSONPathResult("$.regeocode")
	@Get(value = "/v3/geocode/regeo?location={0},{1}")
	AmapResult.Regeocode getLocation(double longitude, double latitude);

	/**
	 * 根据地址获取地理信息（正地理编码）
	 * @param address 地址字符串
	 * @return 地理编码结果列表
	 */
	@ApiOperation("根据经度和纬度获取地理信息")
	@JSONPathResult("$.geocodes")
	@Get(value = "/v3/geocode/geo?address={0}")
	List<AmapResult.Geocode> getLocation(String address);

	/**
	 * 根据起始点和目的地获取步行线路信息
	 * @param origin 起点经纬度
	 * @param destination 终点经纬度
	 * @return 路线信息
	 */
	@ApiOperation("根据起始点和目的地获取线路信息")
	@JSONPathResult("$.route")
	@Get(value = "/v3/direction/walking?origin={0}&destination={1}")
	AmapResult.Route getRoutes(String origin, String destination);

	/**
	 * 计算两个经纬度字符串之间的距离（千米，带单位）
	 * @param location1 起点经纬度字符串
	 * @param location2 终点经纬度字符串
	 * @return 距离字符串（如"1.23km"）
	 */
	@ApiOperation("计算两个经纬度之间的距离（千米）")
	default String distanceKm(String location1, String location2) {
		return distanceKm(Location.of(location1), Location.of(location2));
	}

	/**
	 * 计算两个Location对象之间的距离（千米，带单位）
	 * @param location1 起点
	 * @param location2 终点
	 * @return 距离字符串（如"1.23km"）
	 */
	default String distanceKm(Location location1, Location location2) {
		if (location1 == null || location2 == null) {
			return null;
		}
		double distance = distanceKm(location1.getLongitude(), location1.getLatitude(), location2.getLongitude(), location2.getLatitude());
		return NumberUtil.decimalFormat("#.##", distance) + "km";
	}

	/**
	 * 计算两个Location对象之间的距离（千米，纯数字）
	 * @param location1 起点
	 * @param location2 终点
	 * @return 距离（千米）
	 */
	default double distance(Location location1, Location location2) {
		return distanceKm(location1.getLongitude(), location1.getLatitude(), location2.getLongitude(), location2.getLatitude());
	}

	/**
	 * 计算两个经纬度之间的距离（千米，纯数字）
	 * @param lon1 起点经度
	 * @param lat1 起点纬度
	 * @param lon2 终点经度
	 * @param lat2 终点纬度
	 * @return 距离（千米）
	 */
	@ApiOperation("计算两个经纬度之间的距离")
	default double distanceKm(double lon1, double lat1, double lon2, double lat2) {
		double dLat = Math.toRadians(lat2 - lat1);
		double dLon = Math.toRadians(lon2 - lon1);

		lat1 = Math.toRadians(lat1);
		lat2 = Math.toRadians(lat2);

		double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
		double EARTH_RADIUS = 6371.0; // 地球半径（千米）
		return EARTH_RADIUS * c;
	}

	/**
	 * Forest自定义成功条件：高德API返回status=1才算成功
	 */
	class AmapSuccessCondition extends DefaultSuccessWhen {
		@Override
		public boolean successWhen(ForestRequest req, ForestResponse res) {
			AmapResult amapResult = res.get(AmapResult.class);
			return super.successWhen(req, res) && StrUtil.equals(amapResult.getStatus(), "1");
		}
	}

	/**
	 * Forest拦截器：自动添加key参数，处理异常日志
	 */
	@Component
	class AmapInterceptor implements Interceptor<AmapResult> {
		@Value("${amap.key}")
		private String KEY;

		@Override
		public boolean beforeExecute(ForestRequest request) {
			ForestQueryMap queryMap = request.getQuery();
			queryMap.putIfAbsent("key", KEY);
			return true;
		}

		@Override
		public void onError(ForestRuntimeException ex, ForestRequest request, ForestResponse response) {
			Logger logger = LoggerFactory.getLogger(AmapClient.class);
			logger.error("请求高德地图api出现异常", ex);
		}
	}
}